using Mono.Cecil;
using Mono.Cecil.Cil;

namespace Cil.Ast;

public sealed class FieldSetRefExpression : SingleInstructionExpression, IStaticInstance
{
	public const int InstancePos = 0;
	public const int ValuePos = 1;
	public const int MaxNodes = 2;

	public FieldSetRefExpression(Context context, Instruction position, FieldReference ref_field, Expression exp_value)
		: this(context, position, ref_field, exp_value, new Expression(context))
	{
	}

	public FieldSetRefExpression(Context context, Instruction position, FieldReference ref_field, Expression exp_value, Expression exp_instance)
		: base(context, position)
	{
		if(!exp_instance.ReturnType.HardEquals(ref_field.DeclaringType))
		{
			throw new InvalidOperationException(string.Format("instance field '{0}' is not member of '{1}' type", ref_field.FullName, exp_instance.ReturnType.FullName));
		}

		if(!ref_field.FieldType.HardEquals(exp_value.ReturnType))
		{
			throw new InvalidOperationException(string.Format("instance field type '{0}' is not equals to value type '{1}'", ref_field.FieldType.FullName, exp_value.ReturnType.FullName));
		}

		InstanceExpression = exp_instance;

		// base class
		ReadsStack = InstanceExpression.IsEmpty ? 1 : 2;
		WritesStack = 0;
		ReturnType = Context.MakeRef(typeof(void));
		Nodes = new FixedList<Expression>(MaxNodes);

		// this
		FieldReference = ref_field;
		ValueExpression = exp_value;
	}

	public Expression InstanceExpression  { get { return Nodes[InstancePos]; } set { Nodes[InstancePos] = value; value.ParentNode = this; } }

	public Expression ValueExpression  { get { return Nodes[ValuePos]; } set { Nodes[ValuePos] = value; value.ParentNode = this; } }

	public FieldReference FieldReference { get; private set; }

	#region ICodeDomStaticInstance implementation

	public bool IsStatic 
	{ 
		get {
			return InstanceExpression.IsEmpty;
		}
	}

	#endregion

	public override string ToString()
	{
		return string.Format("{0}.{1} = {2}{3}", InstanceExpression, FieldReference.Name, ValueExpression, FinalString);
	}
}

public static partial class IlAst
{
	public static FieldSetRefExpression FieldSetRef(Context context, Instruction position, FieldReference ref_field, Expression exp_value, Expression exp_instance = null)
	{
		return new FieldSetRefExpression(context, position, ref_field, exp_value, exp_instance);
	}
}